﻿using ChengNet.StrongCaml.Node;
using System;

namespace ChengNet.StrongCaml
{
    #region enums

    /// <summary>
    /// Parameter for <see cref="Membership"/>
    /// </summary>
    public enum MembershipType
    {
        /// <summary>SPWeb.AllUsers</summary>
        SPWebAllUsers,
        /// <summary>SPGroup</summary>
        SPGroup,
        /// <summary>SPWeb.Groups</summary>
        SPWebGroups,
        /// <summary>CurrentUserGroups</summary>
        CurrentUserGroups,
        /// <summary>SPWeb.Users</summary>
        SPWebUsers
    }

    /// <summary>
    /// Optional parameter for <see cref="FieldRef"/> when used within <see cref="OrderBy"/>
    /// </summary>
    public enum SortOrder
    {
        /// <summary>In order</summary>
        Ascending,
        /// <summary>Reverse order</summary>
        Descending
    }

    /// <summary>
    /// Optional parameter for <see cref="M:Value{ValueType.DateTime}"/> specifying whether to query by time as well as the date
    /// </summary>
    public enum TimeValue
    {
        /// <summary>Execute query taking time into account</summary>
        Include,
        /// <summary>Execute query ignoring time</summary>
        Exclude
    }

    #endregion

    /// <summary>
    /// <para>Static factory class for all <a href="http://strongcaml.codeplex.com/">Strong Caml</a> methods.</para>
    /// <para>Start with <c>var camlQuery = <see cref="Query(Where, OrderBy, GroupBy)"/></c> and let IntelliSense guide you.</para>
    /// </summary>
    /// <example>
    /// <code>
    /// var camlQuery = Caml.Query(
    ///                     Caml.Where(
    ///                         
    ///                     )
    ///                 );
    /// </code>
    /// </example>
    public static class Caml
    {
        /// <summary>Major and minor version</summary>
        public const string MajorMinorVersion = "0.1";

        #region General Nodes

        /// <summary>
        /// <para>Top-level tag wrapping the whole CAML query</para>
        /// </summary>
        /// <example><code>
        /// Caml.Query(
        ///     Caml.Where(
        ///         
        ///     )
        /// )
        /// </code><code>
        /// Caml.Query(
        ///     Caml.Where(
        ///         
        ///     ),
        ///     Caml.OrderBy(
        ///             
        ///     )
        /// )
        /// </code><code>
        /// Caml.Query(
        ///     Caml.Where(
        ///         
        ///     ),
        ///     groupBy: Caml.GroupBy(
        ///             
        ///     )
        /// )
        /// </code></example>
        public static Query Query(Where where, OrderBy orderBy = null, GroupBy groupBy = null) { return new Query(where, orderBy, groupBy); }
        //public static Query Query(Literal literal) { return new Query(literal); }

        /// <summary>
        /// <para>Used within the context of a query to specify a filter.</para>
        /// <a href="http://msdn.microsoft.com/en-us/library/ms414805.aspx">MSDN Reference</a>
        /// </summary>
        /// <param name="condition">Either a <see cref="Comparison"/> or a <see cref="LogicalJoin"/></param>
        /// <example><code>
        /// Caml.Where(
        ///     Caml.Eq(
        ///     
        ///     )
        /// )
        /// </code><code>
        /// Caml.Where(
        ///     Caml.And(
        ///         Caml.IsNull(
        ///             
        ///         ),
        ///         Caml.Or(
        ///             Caml.Contains(
        ///                 
        ///             ),
        ///             Caml.Lt(
        ///                 
        ///             )
        ///         )
        ///     )
        /// )
        /// </code></example>
        public static Where Where(Condition condition) { return new Where(condition); }
        //public static Where Where(LogicalJoin logicalJoin) { return new Where(logicalJoin); } // intentional redundancy for IntelliSense
        //public static Where Where(Literal literal) { return new Where(literal); }

        /// <summary>
        /// <para>Represents a reference to a field within a query.</para>
        /// <a href="http://msdn.microsoft.com/en-us/library/ms442728.aspx">MSDN Reference</a>
        /// </summary>
        /// <param name="internalName">The field's internal/static name</param>
        /// <param name="sortType">Specifies sort order</param>
        // TODO: lots of optional attributes
        public static FieldRef FieldRef(string internalName, SortOrder sortType = SortOrder.Ascending) { return new FieldRef(internalName, sortType); }

        #endregion

        #region Group/Order Operators

        /// <summary>
        /// <para>Determines the sort order for a query. The <see cref="OrderBy"/> element contains a group of <see cref="FieldRef"/> elements.</para>
        /// <a href="http://msdn.microsoft.com/en-us/library/ms467378.aspx">MSDN Reference</a>
        /// </summary>
        public static OrderBy OrderBy(params FieldRef[] fieldRefs) { return new OrderBy(fieldRefs); }
        /// <summary>
        /// <para>Contains a Group By section for grouping the data returned through a query in a list view.</para>
        /// <a href="http://msdn.microsoft.com/en-us/library/ms467378.aspx">MSDN Reference</a>
        /// </summary>
        public static GroupBy GroupBy(FieldRef fieldRef) { return new GroupBy(fieldRef); }
        //public static ViewFields ViewFields(params FieldRef[] fieldRefs) { return new ViewFields(fieldRefs); }

        #endregion

        #region <Value> child elements

        /// <summary>
        /// <para>Contains the unique ID number of the currently authenticated user of a site, as defined in the UserInfo table of the content database.</para> http://msdn.microsoft.com/en-us/library/ff625789.aspx
        /// </summary>
        public static UserID UserID() { return new UserID(); }
        /// <summary>
        /// <para>Returns the current date and time.</para> http://msdn.microsoft.com/en-us/library/ms461651.aspx
        /// </summary>
        public static Now Now() { return new Now(); }
        /// <summary>
        /// <para>Renders the current date in the format that is relative to the server's local time zone. For servers in the United States, the format is MM/DD/YYYY (for example, 1/21/2001).</para> http://msdn.microsoft.com/en-us/library/ms460496.aspx
        /// </summary>
        /// <param name="offset">+1 is tomorrow, -1 is yesterday</param>
        public static Today Today(int offset = 0) { return new Today(offset); }

        #endregion

        #region <Value> Overloads

        // Caml ValueType-aware Value elements

        //public static Value<T> Value<T>(bool value) where T : ValueType.Boolean { return new Value<T>(value); }
        /// <overloads>
        /// <para>Contains the value against which the value returned by the <see cref="FieldRef"/> element is compared.</para>
        /// <a href="http://msdn.microsoft.com/en-us/library/ms441886.aspx">MSDN Reference</a>
        /// </overloads>
        /// <summary>
        /// Method for the CAML tag &lt;Value&gt; that's represented in code as an <c>int</c>
        /// </summary>
        /// <typeparam name="T"><see cref="ValueType.Integer"/>, <see cref="ValueType.Lookup"/>, or <see cref="ValueType.User"/></typeparam>
        /// <example><code>
        /// Value&lt;ValueType.Lookup&gt;(14) // in lookup field, the referenced item with ID 14
        /// </code><code>
        /// Value&lt;ValueType.User&gt;(5) // user with ID 5
        /// </code></example>
        public static Value<T> Value<T>(int value) where T : ValueType.Abstract.ValueInt { return new Value<T>(value); }
        /// <summary>
        /// Method for the CAML tag &lt;Value&gt; that's represented in code as a <c>string</c>
        /// </summary>
        /// <typeparam name="T"><see cref="ValueType.Text"/> or <see cref="ValueType.Note"/></typeparam>
        /// <example><code>
        /// Value&lt;ValueType.Note&gt;("This is\na multi-line string.")
        /// </code></example>
        public static Value<T> Value<T>(string str) where T : ValueType.Abstract.ValueString { return new Value<T>(str); }

        //public static Value<T> Value<T>(Today today) where T : ValueType.DateTime { return new Value<T>(today); }
        //public static Value<T> Value<T>(Now now, TimeValue timeValue = TimeValue.Exclude) where T : ValueType.DateTime { return new Value<T>(now, timeValue); }
        //public static Value<T> Value<T>(DateTime dateTime, TimeValue timeValue = TimeValue.Exclude) where T : ValueType.DateTime { return new Value<T>(dateTime, timeValue); }
        /// <summary>
        /// Method for the CAML tag &lt;Value&gt; that's represented in code as a User
        /// </summary>
        /// <typeparam name="T"><see cref="ValueType.Integer"/>, <see cref="ValueType.Lookup"/>, or <see cref="ValueType.User"/></typeparam>
        /// <example><code>
        /// Value&lt;ValueType.Lookup&gt;(
        ///     Caml.UserID()
        /// )
        /// </code></example>
        /// <remarks>agdag</remarks>
        public static Value<T> Value<T>(UserID userId) where T : ValueType.Abstract.ValueUser { return new Value<T>(userId); }

        // default Caml ValueTypes for given C# parameter type
        /// <summary><see cref="bool"/> defaults to ValueType.Boolean</summary>
        public static Value<ValueType.Boolean> Value(bool value) { return new Value<ValueType.Boolean>(value); }
        /// <summary><see cref="int"/> defaults to ValueType.Integer</summary>
        public static Value<ValueType.Integer> Value(int value) { return new Value<ValueType.Integer>(value); }
        /// <summary><see cref="string"/> defaults to ValueType.Text</summary>
        public static Value<ValueType.Text> Value(string str) { return new Value<ValueType.Text>(str); }
        /// <summary><see cref="Today"/> defaults to ValueType.DateTime</summary>
        public static Value<ValueType.DateTime> Value(Today today) { return new Value<ValueType.DateTime>(today); }
        /// <summary><see cref="Now"/> defaults to ValueType.DateTime</summary>
        public static Value<ValueType.DateTime> Value(Now now, TimeValue timeValue = TimeValue.Exclude) { return new Value<ValueType.DateTime>(now, timeValue); }
        /// <summary><see cref="DateTime"/> defaults to ValueType.DateTime</summary>
        public static Value<ValueType.DateTime> Value(DateTime dateTime, TimeValue timeValue = TimeValue.Exclude) { return new Value<ValueType.DateTime>(dateTime, timeValue); }
        /// <summary><see cref="UserID"/> defaults to ValueType.User</summary>
        public static Value<ValueType.User> Value(UserID userId) { return new Value<ValueType.User>(userId); }

        #endregion

        #region Logical joins

        /// <summary>
        /// <para>Used within the <see cref="Where"/> element to group filters in a query.</para>
        /// <a href="http://msdn.microsoft.com/en-us/library/ms472196.aspx">MSDN Reference</a>
        /// </summary>
        public static Or Or(Condition left, Condition right) { return new Or(left, right); }
        /// <summary>
        /// <para>Used within the <see cref="Where"/> element to group filters in a query for a view.</para>
        /// <a href="http://msdn.microsoft.com/en-us/library/ms196939.aspx">MSDN Reference</a>
        /// </summary>
        public static And And(Condition left, Condition right) { return new And(left, right); }
        // intentional redundancy for IntelliSense
        //public static Or Or(Condition left, LogicalJoin right) { return new Or(left, right); }
        //public static Or Or(LogicalJoin left, Condition right) { return new Or(left, right); }
        //public static Or Or(LogicalJoin left, LogicalJoin right) { return new Or(left, right); }
        //public static And And(Condition left, LogicalJoin right) { return new And(left, right); }
        //public static And And(LogicalJoin left, Condition right) { return new And(left, right); }
        //public static And And(LogicalJoin left, LogicalJoin right) { return new And(left, right); }

        #endregion

        #region Comparisons

        /// <summary>
        /// <para>Defines a filter based on the type of membership for the user.</para>
        /// <a href="http://msdn.microsoft.com/en-us/library/aa544234.aspx">MSDN Reference</a>
        /// </summary>
        public static Membership Membership(FieldRef fieldRef, MembershipType type) { return new Membership(fieldRef, type); }

        /// <summary>
        /// <para>Used within a query to return items that are empty (<c>Null</c>).</para>
        /// <a href="http://msdn.microsoft.com/en-us/library/ms462425.aspx">MSDN Reference</a>
        /// </summary>
        public static IsNull IsNull(FieldRef fieldRef) { return new IsNull(fieldRef); }     
        /// <summary>
        /// <para>Used within a query to return items that are not empty (<c>Null</c>).</para>
        /// <a href="http://msdn.microsoft.com/en-us/library/ms465807.aspx">MSDN Reference</a>
        /// </summary>
        public static IsNotNull IsNotNull(FieldRef fieldRef) { return new IsNotNull(fieldRef); }

        /// <overloads>
        /// <para>Arithmetic operator that means "equal to" and is used within a query.</para>
        /// <a href="http://msdn.microsoft.com/en-us/library/ms479601.aspx">MSDN Reference</a>
        /// </overloads>
        /// <summary>Overload for ValueType.Boolean</summary>
        public static Eq Eq(FieldRef fieldRef, Value<ValueType.Boolean> value) { return new Eq(fieldRef, value); }
        /// <summary>Overload for ValueType.Integer</summary>
        public static Eq Eq(FieldRef fieldRef, Value<ValueType.Integer> value) { return new Eq(fieldRef, value); }
        /// <summary>Overload for ValueType.Lookup</summary>
        public static Eq Eq(FieldRef fieldRef, Value<ValueType.Lookup> value) { return new Eq(fieldRef, value); }
        /// <summary>Overload for ValueType.Text</summary>
        public static Eq Eq(FieldRef fieldRef, Value<ValueType.Text> value) { return new Eq(fieldRef, value); }
        /// <summary>Overload for ValueType.Note</summary>
        public static Eq Eq(FieldRef fieldRef, Value<ValueType.Note> value) { return new Eq(fieldRef, value); }
        /// <summary>Overload for ValueType.User</summary>
        public static Eq Eq(FieldRef fieldRef, Value<ValueType.User> value) { return new Eq(fieldRef, value); }
        /// <summary>Overload for ValueType.DateTime</summary>
        public static Eq Eq(FieldRef fieldRef, Value<ValueType.DateTime> value) { return new Eq(fieldRef, value); }

        /// <overloads>
        /// <para>Arithmetic operator that means "not equal to" and is used in queries.</para>
        /// </overloads>
        /// <a href="http://msdn.microsoft.com/en-us/library/ms452901.aspx">MSDN Reference</a>
        /// <summary>Overload for ValueType.Boolean</summary>
        public static Neq Neq(FieldRef fieldRef, Value<ValueType.Boolean> value) { return new Neq(fieldRef, value); }
        /// <summary>Overload for ValueType.Integer</summary>
        public static Neq Neq(FieldRef fieldRef, Value<ValueType.Integer> value) { return new Neq(fieldRef, value); }
        /// <summary>Overload for ValueType.Lookup</summary>
        public static Neq Neq(FieldRef fieldRef, Value<ValueType.Lookup> value) { return new Neq(fieldRef, value); }
        /// <summary>Overload for ValueType.Text</summary>
        public static Neq Neq(FieldRef fieldRef, Value<ValueType.Text> value) { return new Neq(fieldRef, value); }
        /// <summary>Overload for ValueType.Note</summary>
        public static Neq Neq(FieldRef fieldRef, Value<ValueType.Note> value) { return new Neq(fieldRef, value); }
        /// <summary>Overload for ValueType.User</summary>
        public static Neq Neq(FieldRef fieldRef, Value<ValueType.User> value) { return new Neq(fieldRef, value); }
        /// <summary>Overload for ValueType.DateTime</summary>
        public static Neq Neq(FieldRef fieldRef, Value<ValueType.DateTime> value) { return new Neq(fieldRef, value); }

        /// <overloads>
        /// <para>Arithmetic operator that means "less than" and is used in queries in views. This element is used similarly to the <see cref="M:Eq"/> and <see cref="M:Gt"/> elements.</para>
        /// </overloads>
        /// <a href="http://msdn.microsoft.com/en-us/library/ms479398.aspx">MSDN Reference</a>
        /// <summary>Overload for ValueType.Boolean</summary>
        public static Lt Lt(FieldRef fieldRef, Value<ValueType.Boolean> value) { return new Lt(fieldRef, value); }
        /// <summary>Overload for ValueType.Integer</summary>
        public static Lt Lt(FieldRef fieldRef, Value<ValueType.Integer> value) { return new Lt(fieldRef, value); }
        // validity untested
        ///// <summary>Overload for ValueType.Lookup</summary>
        //public static Lt Lt(FieldRef fieldRef, Value<ValueType.Lookup> value) { return new Lt(fieldRef, value); }
        /// <summary>Overload for ValueType.Text</summary>
        public static Lt Lt(FieldRef fieldRef, Value<ValueType.Text> value) { return new Lt(fieldRef, value); }
        /// <summary>Overload for ValueType.Note</summary>
        public static Lt Lt(FieldRef fieldRef, Value<ValueType.Note> value) { return new Lt(fieldRef, value); }
        /// <summary>Overload for ValueType.DateTime</summary>
        public static Lt Lt(FieldRef fieldRef, Value<ValueType.DateTime> value) { return new Lt(fieldRef, value); }

        /// <overloads>
        /// <para>Arithmetic operator that means "greater than." This element is used similarly to the <see cref="M:Eq"/> and <see cref="M:Lt"/> elements.</para>
        /// </overloads>
        /// <a href="http://msdn.microsoft.com/en-us/library/ms458990.aspx">MSDN Reference</a>
        /// <summary>Overload for ValueType.Boolean</summary>
        public static Gt Gt(FieldRef fieldRef, Value<ValueType.Boolean> value) { return new Gt(fieldRef, value); }
        /// <summary>Overload for ValueType.Integer</summary>
        public static Gt Gt(FieldRef fieldRef, Value<ValueType.Integer> value) { return new Gt(fieldRef, value); }
        // validity untested
        ///// <summary>Overload for ValueType.Lookup</summary>
        //public static Gt Gt(FieldRef fieldRef, Value<ValueType.Lookup> value) { return new Gt(fieldRef, value); }
        /// <summary>Overload for ValueType.Text</summary>
        public static Gt Gt(FieldRef fieldRef, Value<ValueType.Text> value) { return new Gt(fieldRef, value); }
        /// <summary>Overload for ValueType.Note</summary>
        public static Gt Gt(FieldRef fieldRef, Value<ValueType.Note> value) { return new Gt(fieldRef, value); }
        /// <summary>Overload for ValueType.DateTime</summary>
        public static Gt Gt(FieldRef fieldRef, Value<ValueType.DateTime> value) { return new Gt(fieldRef, value); }

        /// <overloads>
        /// <para>Arithmetic operator that means "less than or equal to." The <see cref="M:Leq"/> element is used in view queries similarly to the <see cref="M:Eq"/> and <see cref="M:Geq"/> elements.</para>
        /// </overloads>
        /// <a href="http://msdn.microsoft.com/en-us/library/ms431787.aspx">MSDN Reference</a>
        /// <summary>Overload for ValueType.Boolean</summary>
        public static Leq Leq(FieldRef fieldRef, Value<ValueType.Boolean> value) { return new Leq(fieldRef, value); }
        /// <summary>Overload for ValueType.Integer</summary>
        public static Leq Leq(FieldRef fieldRef, Value<ValueType.Integer> value) { return new Leq(fieldRef, value); }
        // validity untested
        ///// <summary>Overload for ValueType.Lookup</summary>
        //public static Leq Leq(FieldRef fieldRef, Value<ValueType.Lookup> value) { return new Leq(fieldRef, value); }
        /// <summary>Overload for ValueType.Text</summary>
        public static Leq Leq(FieldRef fieldRef, Value<ValueType.Text> value) { return new Leq(fieldRef, value); }
        /// <summary>Overload for ValueType.Note</summary>
        public static Leq Leq(FieldRef fieldRef, Value<ValueType.Note> value) { return new Leq(fieldRef, value); }
        /// <summary>Overload for ValueType.DateTime</summary>
        public static Leq Leq(FieldRef fieldRef, Value<ValueType.DateTime> value) { return new Leq(fieldRef, value); }

        /// <overloads>
        /// <para>Arithmetic operator that means "greater than or equal to." This element can be used within a <see cref="Where"/> element in a query.</para>
        /// </overloads>
        /// <a href="http://msdn.microsoft.com/en-us/library/ms416296.aspx">MSDN Reference</a>
        /// <summary>Overload for ValueType.Boolean</summary>
        public static Geq Geq(FieldRef fieldRef, Value<ValueType.Boolean> value) { return new Geq(fieldRef, value); }
        /// <summary>Overload for ValueType.Integer</summary>
        public static Geq Geq(FieldRef fieldRef, Value<ValueType.Integer> value) { return new Geq(fieldRef, value); }
        // validity untested
        ///// <summary>Overload for ValueType.Lookup</summary>
        //public static Geq Geq(FieldRef fieldRef, Value<ValueType.Lookup> value) { return new Geq(fieldRef, value); }
        /// <summary>Overload for ValueType.Text</summary>
        public static Geq Geq(FieldRef fieldRef, Value<ValueType.Text> value) { return new Geq(fieldRef, value); }
        /// <summary>Overload for ValueType.Note</summary>
        public static Geq Geq(FieldRef fieldRef, Value<ValueType.Note> value) { return new Geq(fieldRef, value); }
        /// <summary>Overload for ValueType.DateTime</summary>
        public static Geq Geq(FieldRef fieldRef, Value<ValueType.DateTime> value) { return new Geq(fieldRef, value); }

        /// <overloads>
        /// <para>Searches for a string anywhere within a column that holds <see cref="ValueType.Text"/> or <see cref="ValueType.Note"/> field type values.</para>
        /// </overloads>
        /// <a href="http://msdn.microsoft.com/en-us/library/ms196501.aspx">MSDN Reference</a>
        /// <summary>Overload for ValueType.Text</summary>
        public static Contains Contains(FieldRef fieldRef, Value<ValueType.Text> value) { return new Contains(fieldRef, value); }
        /// <summary>Overload for ValueType.Note</summary>
        public static Contains Contains(FieldRef fieldRef, Value<ValueType.Note> value) { return new Contains(fieldRef, value); }

        /// <overloads>
        /// <para>Searches for a string at the start of a column that holds <see cref="ValueType.Text"/> or <see cref="ValueType.Note"/> field type values.</para>
        /// </overloads>
        /// <a href="http://msdn.microsoft.com/en-us/library/ms476051.aspx">MSDN Reference</a>
        /// <summary>Overload for ValueType.Text</summary>
        public static BeginsWith BeginsWith(FieldRef fieldRef, Value<ValueType.Text> value) { return new BeginsWith(fieldRef, value); }
        /// <summary>Overload for ValueType.Note</summary>
        public static BeginsWith BeginsWith(FieldRef fieldRef, Value<ValueType.Note> value) { return new BeginsWith(fieldRef, value); }

        // TODO: untested
        //public static DateRangesOverlap DateRangesOverlap(FieldRef fieldRef, Value<ValueType.DateTime> value) { return new DateRangesOverlap(fieldRef, value); }

        #endregion
    }
}
